// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"runtime/internal/sys"
	"unsafe"
)

// 可能在STW期间运行，因此不允许使用写屏障。
// 
// go:nowritebarrierrec 
func sighandler(_ureg *ureg, note *byte, gp *g) int {
	_g_ := getg()
	var t sigTabT
	var docrash bool
	var sig int
	var flags int
	var level int32

	c := &sigctxt{_ureg}
	notestr := gostringnocopy(note)

	// 内核永远不会向我们传递零音符或ureg，因此我们可能在sigtramp的某个地方犯了错误。
	if _ureg == nil || note == nil {
		print("sighandler: ureg ", _ureg, " note ", note, "\n")
		goto Throw
	}
	// 检查注释是否不超过ERRMAX字节（包括
	// 尾随NUL）。我们不应该收到更长的钞票。
	if len(notestr) > _ERRMAX-1 {
		print("sighandler: note is longer than ERRMAX\n")
		goto Throw
	}
	if isAbortPC(c.pc()) {
		// 永远不要将中止变成恐慌。
		goto Throw
	}
	// 查看注释是否与sigtab中的某个模式匹配。
	// 程序可以在更高的
	// 级别处理与任何模式都不匹配的注释，但在其他情况下将被忽略。
	flags = _SigNotify
	for sig, t = range sigtable {
		if hasPrefix(notestr, t.name) {
			flags = t.flags
			break
		}
	}
	if flags&_SigPanic != 0 && gp.throwsplit {
		// 我们不能安全地使用sigpanic，因为它可能会增加
		// 堆栈。而是在信号处理程序中中止。
		flags = (flags &^ _SigPanic) | _SigThrow
	}
	if flags&_SigGoExit != 0 {
		exits((*byte)(add(unsafe.Pointer(note), 9))) // 去掉“开始：退出”前缀。
	}
	if flags&_SigPanic != 0 {
		// 将错误字符串从sigtramp的堆栈复制到m->notesig中，这样我们就可以从紧急例程可靠地访问它。
		memmove(unsafe.Pointer(_g_.m.notesig), unsafe.Pointer(note), uintptr(len(notestr)+1))
		gp.sig = uint32(sig)
		gp.sigpc = c.pc()

		pc := c.pc()
		sp := c.sp()

		// 如果我们不将PC识别为代码
		// 但我们确实将堆栈上的顶部指针识别为代码
		// 那么假设这是对非代码的调用，并将其视为
		// PC==0，以使展开显示上下文。
		if pc != 0 && !findfunc(pc).valid() && findfunc(*(*uintptr)(unsafe.Pointer(sp))).valid() {
			pc = 0
		}

		// 如果存在LR，则在进入SIGMANIC之前，SIGMANICTRAMP必须将其保存到堆栈
		// 以便正确处理叶
		// 函数中的恐慌。这将粉碎堆栈帧，但无论如何我们不会回到那里。
		if usesLR {
			c.savelr(c.lr())
		}

		// 如果PC==0，可能会因为调用nil func而惊慌失措。
		// 不要假装，因为返回地址会使跟踪看起来像是一个呼叫
		// 而不是sigmanic。（否则跟踪将以
		// sigmanic结束，我们将无法看到谁出错）。
		if pc != 0 {
			if usesLR {
				c.setlr(pc)
			} else {
				sp -= sys.PtrSize
				*(*uintptr)(unsafe.Pointer(sp)) = pc
				c.setsp(sp)
			}
		}
		if usesLR {
			c.setpc(funcPC(sigpanictramp))
		} else {
			c.setpc(funcPC(sigpanic0))
		}
		return _NCONT
	}
	if flags&_SigNotify != 0 {
		if ignoredNote(note) {
			return _NCONT
		}
		if sendNote(note) {
			return _NCONT
		}
	}
	if flags&_SigKill != 0 {
		goto Exit
	}
	if flags&_SigThrow == 0 {
		return _NCONT
	}
Throw:
	_g_.m.throwing = 1
	_g_.m.caughtsig.set(gp)
	startpanic_m()
	print(notestr, "\n")
	print("PC=", hex(c.pc()), "\n")
	print("\n")
	level, _, docrash = gotraceback()
	if level > 0 {
		goroutineheader(gp)
		tracebacktrap(c.pc(), c.sp(), c.lr(), gp)
		tracebackothers(gp)
		print("\n")
		dumpregs(_ureg)
	}
	if docrash {
		crash()
	}
Exit:
	goexitsall(note)
	exits(note)
	return _NDFLT // 未到达
}

func sigenable(sig uint32) {
}

func sigdisable(sig uint32) {
}

func sigignore(sig uint32) {
}

func setProcessCPUProfiler(hz int32) {
}

func setThreadCPUProfiler(hz int32) {
	// TODO:启用分析中断。
	getg().m.profilehz = hz
}

// gsignalStack在计划9中未使用。
type gsignalStack struct{}
